package com.hq.base.web;

import com.hq.common.utils.PropertiesCryptConvert;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.WebUtils;

import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

/**

 * @since 2015-02-07 0:00
 */
public class WebAppRootListener implements ServletContextListener {
    private static final String WEB_APP_ROOT_KEY_PARAM = "webAppRootKey";
    private static final String DEFAULT_WEB_APP_ROOT_KEY = "webapp.root";
    private static final String SYS_CONFIG_CONTEXT_PARAM = "SYS_CONFIG";


    public void contextInitialized(ServletContextEvent event) {
        String rootPath = this.setWebAppRoot(event.getServletContext());
        this.readProperties(event.getServletContext());
        this.fixTime();
 }

    /**
     * 需要注意的是，在成功设置webAppRootKey前，不能有地方创建logger对象，不然日志文件占位符还未生效，会在非预期的地方创建日志
     */
    private String setWebAppRoot(ServletContext servletContext) throws IllegalStateException {
        String rootPath = servletContext.getRealPath("/");
        if (rootPath == null) {
            throw new IllegalStateException("Cannot set web app root system property when WAR file is not expanded");
        }
        String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);
        String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);
        String oldValue = System.getProperty(key);
        if (oldValue != null && !StringUtils.pathEquals(oldValue, rootPath)) {
            throw new IllegalStateException("Web app root system property already set to different value: '" + key + "' = [" + oldValue + "] instead of [" + rootPath + "] - Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");
        }
        System.setProperty(key, rootPath);
        return rootPath;
    }

    private void readProperties(ServletContext servletContext) {
        String param = servletContext.getInitParameter(SYS_CONFIG_CONTEXT_PARAM);
        String[] configPaths = param.split(",");
        for (String path : configPaths) {
            String configPath = servletContext.getRealPath(path);
            if(new File(configPath).exists()) {
                PropertiesCryptConvert convert = new PropertiesCryptConvert(configPath);
                Properties pro = convert.getProperties(true);
                //将数据库配置保存到系统属性中
                this.storeProperties2SysEnv(pro);
            }
        }
    }

    private void storeProperties2SysEnv(Properties properties) {
        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
            System.setProperty(entry.getKey().toString(), entry.getValue().toString());
        }
    }

    private void fixTime() {
        long dbCurrentTime = this.getDbSysDate();
    }

    public void contextDestroyed(ServletContextEvent event) {
        WebUtils.removeWebAppRootSystemProperty(event.getServletContext());
        this.destroy(event.getServletContext());
    }

    /**
     * 获取数据库毫秒值
     */
    private long getDbSysDate() {
        Timestamp dbTime = null;
        Connection conn = null;
        Statement statement = null;
        ResultSet rs = null;
        try {
            String jndiName = System.getProperty("jdbc.jndiname");
            if (jndiName == null || jndiName.equals("")) {
                String driverClassName = System.getProperty("jdbc.driverClassName");
                Class.forName(driverClassName);
                String url = System.getProperty("jdbc.url");
                String userName = System.getProperty("jdbc.username");
                String password = System.getProperty("jdbc.password");
                conn = DriverManager.getConnection(url, userName, password);
            } else {
                InitialContext ctx = new InitialContext();
                DataSource ds = (DataSource) ctx.lookup(jndiName);
                if (ds == null) {
                    //尝试Tomcat
                    ds = (DataSource) ctx.lookup("java:comp/env/" + jndiName);
                }
                conn = ds.getConnection();
            }
            statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            rs = statement.executeQuery("select now() as dbTime from dual");
            while (rs.next()) {
                dbTime = rs.getTimestamp("dbTime");
            }
        } catch (Exception e) {
//            System.err.println("初始化数据库连接失败！");
            e.printStackTrace();
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (statement != null) {
                    statement.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (dbTime == null) {
            System.out.println("获取数据库时间失败！取本机时间替代。");
            return new java.util.Date().getTime();
        }
        return dbTime.getTime();
    }

    private Properties loadAllProperties(String resourceName) throws IOException {
        Assert.notNull(resourceName, "Resource name must not be null");
        Properties props = new Properties();
        FileInputStream in = new FileInputStream(resourceName);
        props.load(in);
        in.close();
        return props;
    }

    public void destroy(ServletContext servletContext) {
        String prefix = getClass().getSimpleName() + " destroy() ";
        try {
            Enumeration<Driver> drivers = DriverManager.getDrivers();
            while (drivers.hasMoreElements()) {
                DriverManager.deregisterDriver(drivers.nextElement());
            }
        } catch (Exception e) {
            servletContext.log(prefix + "Exception caught while deregistering JDBC drivers", e);
        }
        servletContext.log(prefix + "complete");
    }
}
